
#include "peripheral.h"
#include "contract.h"
#include "base.h"
#include "clock_countor.h"
#include "bsp.h"

static void ClockCountor_ConfigPeak(uint32_t max_clock);


#define CLOCK_TIMER			TIMER2
#define CLOCK_TIMER_IRQn	TIMER2_IRQn
#define CLOCK_TIMER_CLK_ENABLE() rcu_periph_clock_enable(RCU_TIMER2)
#define CLOCK_TIMER_CLK_FREQ()	rcu_clock_freq_get(CK_APB1)

#define CLOCK_TIMER_CLEAR_COUNTOR()	timer_counter_value_config(CLOCK_TIMER, 0)
#define CLOCK_TIMER_SET_COUNTOR_PEAK(cnt) 	do{TIMER_CAR(CLOCK_TIMER) = (uint32_t)cnt;}while(0)
#define CLOCK_TIMER_GET_COUNTOR_PEAK() TIMER_CAR(CLOCK_TIMER)

#define ERROR_RANGE	4


static volatile uint32_t CounterDivideCardClock;
static volatile uint32_t ClockCounter;
static volatile uint32_t CounterPeak;
static volatile uint32_t CountMultiple = 0;
static volatile uint32_t CurrentCountTop = 0;

static vfun_t timeout_fun;

void TIMER2_IRQHandler(void)
{
    if(SET == timer_interrupt_flag_get(CLOCK_TIMER, TIMER_INT_UP)){
        uint32_t peak;
        uint32_t count;
        timer_interrupt_flag_clear(CLOCK_TIMER, TIMER_INT_UP);

        count = ClockCounter;
        peak = CurrentCountTop;

        if(count > peak)
        {
            count -= peak;
        }
        else
        {
            count = 0;
        }

        if(count < ERROR_RANGE)/*误差1个卡时钟*/
        {
            if(CountMultiple > 0)
            {
                ClockCounter = CounterPeak + count;
                CountMultiple--;
            }
            else
            {
				timer_interrupt_disable(CLOCK_TIMER, TIMER_INT_UP);
				timer_disable(CLOCK_TIMER);
                ClockCounter = 0;
                
                if(timeout_fun != (vfun_t)0)
                {
                    timeout_fun();
                }
            }
        }
        else
        {
            ClockCounter = count;
            ClockCountor_ConfigPeak(ClockCounter);
        }

    }
}


void ClockCountor_Init(uint32_t frequency)
{
    timer_parameter_struct timer_initpara;
	uint32_t uclk;

	nvic_irq_enable(CLOCK_TIMER_IRQn, 2, 0);
	
    CLOCK_TIMER_CLK_ENABLE();
	
    timer_deinit(CLOCK_TIMER);
    /* initialize TIMER init parameter struct */
    timer_struct_para_init(&timer_initpara);

	uclk = CLOCK_TIMER_CLK_FREQ();
    timer_initpara.prescaler         = (uclk / frequency) - 1;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 100;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_init(CLOCK_TIMER, &timer_initpara);
	
	timer_auto_reload_shadow_disable(CLOCK_TIMER);
    timer_interrupt_enable(CLOCK_TIMER, TIMER_INT_UP);
}


static void ClockCountor_ConfigPeak(uint32_t max_clock)
{
    uint32_t count;

    count = min(max_clock, 65536);
    CurrentCountTop = count;
    CLOCK_TIMER_SET_COUNTOR_PEAK((uint16_t)(count - 1));
}

void ClockCountor_SetCountMultiple(uint32_t multiple)
{
    CountMultiple = multiple;
}

uint32_t ClockCountor_GetFrequency(void)
{
    return rcu_clock_freq_get(CK_APB1);
}

void ClockCountor_SetCardClockFrequency(uint32_t frequency)
{
    uint16_t prescaler;
    prescaler = (CLOCK_TIMER_CLK_FREQ() / frequency) - 1;
	timer_prescaler_config(CLOCK_TIMER, prescaler, TIMER_PSC_RELOAD_NOW);
}

/*
	timeout_action为一个函数指针，当ClockCountor溢出时，该函数指针所指函数被执行
*/
void ClockCountor_Start(uint32_t clock, vfun_t timeout_action)
{
    if(clock < ERROR_RANGE)
    {
        ClockCounter = 0;

        if(timeout_action != (vfun_t)0)
        {
            timeout_action();
        }

        return;
    }

    timeout_fun = timeout_action;
    CounterPeak =  clock;
    ClockCounter = clock;
    
    CLOCK_TIMER_CLEAR_COUNTOR();
    ClockCountor_ConfigPeak(clock);
    timer_interrupt_enable(CLOCK_TIMER, TIMER_INT_UP);
	timer_enable(CLOCK_TIMER);
}

void ClockCountor_Reset(void)
{
	CLOCK_TIMER_CLEAR_COUNTOR();
    ClockCounter = CounterPeak;
    CountMultiple = 0;
}

uint8_t ClockCountor_Check(void)
{
    return ClockCounter == 0;
}

void ClockCountor_Stop(void)
{
    timer_interrupt_disable(CLOCK_TIMER, TIMER_INT_UP);
    timer_disable(CLOCK_TIMER);
	CLOCK_TIMER_CLEAR_COUNTOR();
    CountMultiple = 0;
    timeout_fun = 0;
}

static void dummy_action(void)
{

}

void ClockCountor_Wait(uint32_t clock)
{
    ClockCountor_Start(clock, dummy_action);

    while(ClockCounter != 0);

    ClockCountor_Stop();
}

#ifndef NDEBUG
void test_ClockCountor(void)
{
    ClockCountor_Wait(10000000);
}
#endif
